<>
  <head>
    <title>
      发布分布式事件
    </title>
  </head>
  <body>
    <script>
      
                    /*
                        二、发布分布式事件  Publishing Events
                        
                                以下部分解释了发布分布式事件的两种方法：

                                    1、使用IDistributedEventBus 发布事件

                                                ①、如何发布事件
                                                
                                                                IDistributedEventBus可以注入并用于发布分布式事件。

                                                                eg:  当产品的库存数量发生变化时发布分布式事件

                                                                        using System;
                                                                        using System.Threading.Tasks;
                                                                        using Volo.Abp.DependencyInjection;
                                                                        using Volo.Abp.EventBus.Distributed;

                                                                        namespace AbpDemo
                                                                        {
                                                                            public class MyService : ITransientDependency
                                                                            {
                                                                                private readonly IDistributedEventBus _distributedEventBus;

                                                                                public MyService(IDistributedEventBus distributedEventBus)
                                                                                {
                                                                                    _distributedEventBus = distributedEventBus; // 注入分布式事件总线服务
                                                                                }
                                                                                
                                                                                public virtual async Task ChangeStockCountAsync(Guid productId, int newCount)
                                                                                {
                                                                                    await _distributedEventBus.PublishAsync( // 发布分布式事件
                                                                                        new StockCountChangedEto  // 传入事件事件传输对象
                                                                                        {
                                                                                            ProductId = productId,
                                                                                            NewCount = newCount
                                                                                        }
                                                                                    );
                                                                                }
                                                                            }
                                                                        }

                                                                PublishAsync方法获取事件对象，该对象负责保存与事件相关的数据。它是一个简单的普通类：

                                                                        using System;

                                                                        namespace AbpDemo
                                                                        {
                                                                            [EventName("MyApp.Product.StockChange")]
                                                                            public class StockCountChangedEto
                                                                            {
                                                                                public Guid ProductId { get; set; }
                                                                                
                                                                                public int NewCount { get; set; }
                                                                            }
                                                                        }

                                                                即使你不需要传输任何数据，你也需要创建一个类（在这种情况下是一个空类）。


                                                        ★、Eto是我们按惯例使用的EventTransferObject的后缀。虽然不是必需的，但我们发现识别此类事件类很有用（就像应用层上的DTO一样）。


                                                ②、[EventName] 特性

                                                                EventName特性是可选的，但是建议使用它。

                                                                如果你不为事件类型使用它(ETO类)，则事件名称将是事件类的全名，AbpDemo.StockCountChangedEto在这种情况下。


                                                ③、关于事件对象的序列化

                                                                事件传输对象（ETO）必须是可序列化的，因为当它被传输到进程外时，它们将被序列化/反序列化为JSON或其他格式。

                                                                避免循环引用、多态、私有setter并提供默认（空）构造函数，
                                                                
                                                                如果你有任何其他构造函数作为良好的实践（而某些序列化程序可能会容忍它），就像DTO一样。

                                
                                2、在实体/聚合根类中发布事件

                                            实体不能通过依赖注入注入服务，但在实体/聚合根中发布分布式事件是很常见的。

                                        ①、如何在实体/聚合根中发布事件

                                                    eg： 在聚合根方法中发布分布式事件

                                                            using System;
                                                            using Volo.Abp.Domain.Entities;

                                                            namespace AbpDemo
                                                            {
                                                                public class Product : AggregateRoot<Guid>
                                                                {
                                                                    public string Name { get; set; }
                                                                    
                                                                    public int StockCount { get; private set; }

                                                                    private Product() { }

                                                                    public Product(Guid id, string name)
                                                                        : base(id)
                                                                    {
                                                                        Name = name;
                                                                    }

                                                                    // 带有分布式事件的属性更改方法
                                                                    public void ChangeStockCount(int newCount)
                                                                    {
                                                                        StockCount = newCount;
                                                                        
                                                                        // 添加(发布)分布式事件
                                                                        AddDistributedEvent(
                                                                            new StockCountChangedEto
                                                                            {
                                                                                ProductId = Id,
                                                                                NewCount = newCount
                                                                            }
                                                                        );
                                                                    }
                                                                }
                                                            }

                                                   ★、 AggregateRoot类定义了AddDistributedEvent以添加一个新的分布式事件，该事件在聚合根对象保存（创建、更新或删除）到数据库时发布。


                                                   ★、如果实体发布了这样的事件，最好以受控的方式更改相关属性，就像上面的示例-StockCount只能通过保证发布事件的ChangeStockCount方法进行更改。

                                        ②、IGeneratesDomainEvents接口 

                                                    实际上，添加分布式事件并不是AggregateRoot类独有的。

                                                    你可以为任何实体类实现IGeneratesDomainEvents。

                                                    但是，AggregateRoot默认实现它，让你轻松。

                                                    
                                                    ★、不建议为非聚合根的实体实现此接口，因为它可能不适用于此类实体的某些数据库提供程序。它适用于EF Core，但不适用于MongoDB。

                                                    EF Core可以用！！ 我得用！！

                                        ③、工作原理

                                                    调用AddDistributedEvent不会立即发布事件。  
                                                    
                                                    当你将更改保存到数据库时，事件将被发布：

                                                         对于EF Core，它发布在DbContext.SaveChanges上。

                                                         对于MongoDB，当你调用存储库的InsertAsync、UpdateAsync或DeleteAsync方法时会发布它（因为MongoDB没有更改跟踪系统）。


                    */
    </script>
  </body>
</html>